#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>  // shared memory
#include<sys/sem.h>  // semaphore
#include<sys/msg.h>  // message queue
#include<string.h>   // memcpy

// 消息队列结构
struct msg_form
{
	long mtype;
	char mtext;
};

// 联合体，用于semctl初始化
union semun
{
	int              val; /*for SETVAL*/
	struct semid_ds *buf;
	unsigned short  *array;
};

// P操作:
// 若信号量值为1，获取资源并将信号量值-1 
// 若信号量值为0，进程挂起等待
int sem_p(int sem_id)
{
	struct sembuf sbuf;
	sbuf.sem_num = 0; /*序号*/
	sbuf.sem_op = -1; /*P操作*/
	sbuf.sem_flg = SEM_UNDO;

	if (semop(sem_id, &sbuf, 1) == -1)
	{
		perror("P operation Error");
		return -1;
	}
	return 0;
}

// V操作：
// 释放资源并将信号量值+1
// 如果有进程正在挂起等待，则唤醒它们
int sem_v(int sem_id)
{
	struct sembuf sbuf;
	sbuf.sem_num = 0; /*序号*/
	sbuf.sem_op = 1;  /*V操作*/
	sbuf.sem_flg = SEM_UNDO;

	if (semop(sem_id, &sbuf, 1) == -1)
	{
		perror("V operation Error");
		return -1;
	}
	return 0;
}


int main()
{
	key_t key;
	int shmid, semid, msqid;
	char *shm;
	struct msg_form msg;
	//while循环条件
	int flag = 1;

	// 获取key值
	if ((key = ftok(".", 'z')) < 0)
	{
		perror("ftok error");
		exit(1);
	}

	// 获取共享内存
	if ((shmid = shmget(key, 1024, 0)) == -1)
	{
		perror("shmget error");
		exit(1);
	}

	// 连接共享内存
	shm = (char*)shmat(shmid, 0, 0);
	if (shm == (void*)-1)
	{
		perror("Attach Shared Memory Error");
		exit(1);
	}

	// 创建消息队列
	if ((msqid = msgget(key, 0)) == -1)
	{
		perror("msgget error");
		exit(1);
	}

	// 获取信号量
	if ((semid = semget(key, 0, 0)) == -1)
	{
		perror("semget error");
		exit(1);
	}

	// 写数据
	printf("***************************************\n");
	printf("*                 IPC                 *\n");
	printf("*    Input r to send data to server.  *\n");
	printf("*    Input q to quit.                 *\n");
	printf("***************************************\n");

	while (flag)
	{
		char c;
		printf("Please input command: ");
		scanf("%c", &c);
		switch (c)
		{
		case 'r':
			printf("Data to be send: ");
			//访问资源
			sem_p(semid);
			scanf("%s", shm);
			//释放资源
			sem_v(semid);
			//清空标准输入缓冲区
			while ((c = getchar()) != '\n' && c != EOF);
			msg.mtype = 888;
			//发送消息通知服务器读数据
			msg.mtext = 'r';
			msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
			break;
		case 'q':
			msg.mtype = 888;
			msg.mtext = 'q';
			msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
			flag = 0;
			break;
		default:
			printf("Wrong input!\n");
			//清空标准输入缓冲区
			while ((c = getchar()) != '\n' && c != EOF);
		}
	}

	// 断开连接
	shmdt(shm);

	return 0;
}
